package cn.ljy.common.utils.file;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.ljy.common.exception.model.MyFileException;
import cn.ljy.common.exception.model.MyWebException;
import cn.ljy.common.utils.requests.RequestUtil;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

public class MyFileUpload {

	//获取允许上传的文件类型，根据kindEditor方法设计，需要考虑大小写。也可用于其它上传组件，现在为本站所用的上传模式
	public final static HashMap<String, String> EXT_MAP = new HashMap<String, String>(){
		{
			put("image", "gif,jpg,jpeg,pjpeg,png,bmp,webp,jfif,tif,ico");
			put("flash", "swf,flv");
			put("media", "swf,flv,mp3,mp4,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb");
			put("file", "doc,docx,xls,xlsx,ppt,pptx,htm,html,txt,zip,rar,gz,bz2,chm,txt,pdf");
//			put("table", "xls,xlsx");
		}
	};

	/**
	 * 检查dirName是否是允许的格式，如果为空则默认所有允许的后缀
	 * 根据dirName，判断后缀名是否是dirName允许的格式
	 * @param ext 后缀名 如jpg，txt
	 * @param dirName 目录名  如image,file。如果是null则默认为上传图片
	 * @return true表示允许
	 * @Throws MyFormPropertyException 检查从web层传递过来的值是否合法
	 */
	public static boolean validateExtByDir(String ext,String dirName) throws MyWebException {
		boolean judge=false;
		if(StringUtils.hasLength(ext)){
			// 统一将后缀名改为小写字母
			ext=ext.trim().toLowerCase();
			if(StringUtils.hasLength(dirName)){
				Map<String, String> extMap = EXT_MAP;//获取项目中设置的上传规则
				if(extMap.keySet().contains(dirName)){//如果dirName属于网站的对应目录
					// 获取上传文件对应的允许后缀名集合
					String[] exts = extMap.get(dirName).split(",");
					List<String> extList = Arrays.<String> asList(exts);// 将数组转换为list
					if (!extList.contains(ext)) {// 如果允许扩展名中不包含上传文件的扩展名
						throw new MyWebException("上传文件扩展名是不允许的扩展名。\n只允许" + extMap.get(dirName) + "格式。");
					}else{//如果后缀名符合网站要求
						judge=true;
					}
				}else{//如果dirName不属于网站的对应目录
					throw new MyWebException("上传文件类型dirName名称错误");
				}
			}else{ // 如果为空则默认所有允许的后缀
				Set<String> allExtSet=new HashSet<>();//用于后面通过解析获取所有网站允许的后缀名
				Collection<String> allExts = EXT_MAP.values();
				for(String extsString:allExts){
					String[] extArray=extsString.split(",");//将gif,jpg,jpeg,png,bmp字符串拆分为数组
					Set<String> extSet = Arrays.stream(extArray).collect(Collectors.toSet());//转换为set集合
					allExtSet.addAll(extSet);
				}
				if(allExtSet.contains(ext)){ // 如果包含后缀名
					judge=true;
				}else{//如果dirName不属于网站的对应目录
					throw new MyWebException("上传的文件不符合本站规则。\n只允许" + allExtSet + "格式。");
				}
			}
		}else{
			throw new MyWebException("文件后缀名不能为空");
		}
		return judge;
	}

	/**
	 * 上传文件到网站的指定目录，并返回上传成功后的网址。
	 * 说明：网址必须在web项目下才生效
	 * @param file 上传的文件
	 * @param dirName 目录名  如image,file。如果是null则默认为所有文件
	 * @param savePath 要在服务端存储的物理路径，例如： c:/university_empolyment_management/upload/resource/
	 * @param baseUploadUrl 图片上传后回显网址的路径父目录。例如：resource/，那么回显的网址就是：http://localhost:8080/upload/resource/b17f24ff026d40949c85a24f4f375d42.jpg
	 * @param createDateFolder 是否对上传的文件按日期进行归类，true表示按。建议资源类文件夹按日期归类，用户头像等不需要
	 * @param allowUploadSize 允许上传的文件大小，单位为B
	 * @return 上传成功后的网址
	 */
	public static MyFileModel uploadMultipartFile(MultipartFile file,String dirName,String savePath,String baseUploadUrl,boolean createDateFolder,long allowUploadSize) {
		//首先验证文件大小
		if(file.getSize()>allowUploadSize){
			throw new MyFileException("上传失败：上传文件大小为"+file.getSize()+"B，超过了网站允许上传的文件大小"+allowUploadSize+"B");
		}
		MyFileModel myFileModel=new MyFileModel();//返回上传后的文件信息
		//定义文件上传后的相对物理路径,例如：2022/11/9bb08e79fa30492691af192705657af6.mp4
		StringBuilder fileRelativePath=new StringBuilder();
		if(file!=null&&!file.isEmpty()){// 如果验证无误，则执行上传
			//创建存储目录:在第一次上传时生效
			FileUtil.mkdir(savePath);
			//获取文件大小
			myFileModel.setFileSize(file.getSize());
			//获取文件名
			String fileName=file.getOriginalFilename();
			myFileModel.setFileNameOfOrigin(fileName);
			//获取文件扩展名
			String extensionName=FileUtil.extName(fileName);//获取文件扩展名
			if(StringUtils.hasLength(extensionName)){//如果有扩展名
				//将扩展名统一改为小写字母
				extensionName=extensionName.toLowerCase();
			}
			//先按照资源文件夹分类存放
			if(dirName==null){
				dirName="image";
			}
			//设置文件类型
			myFileModel.setFileType(dirName);
			savePath += dirName + "/";
			// 验证上传文件后缀名是否符合本站要求
			if(MyFileUpload.validateExtByDir(extensionName,dirName)){
				baseUploadUrl += dirName+ "/";
				fileRelativePath.append(dirName+"/");
				//将资源按照日期归类存放，例如当天的资源就存储在当天
				if(createDateFolder){
					Date today=new Date();
					//1.将文件夹按年存放
					SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
					String year = yearFormat.format(today);
					savePath += year + "/";
					baseUploadUrl += year + "/";
					fileRelativePath.append(year + "/");
					//创建存储目录:在第一次上传时生效
					FileUtil.mkdir(savePath);
					//2.将文件夹再按月存放
					SimpleDateFormat monthFormat = new SimpleDateFormat("MM");
					String month = monthFormat.format(today);
					savePath += month + "/";
					baseUploadUrl += month + "/";
					fileRelativePath.append(month + "/");
					//创建存储目录:在第一次上传时生效
					FileUtil.mkdir(savePath);
				}
				//使用随机数生成新的文件名：生成的是不带-的字符串，类似于：b17f24ff026d40949c85a24f4f375d42
//				fileName = IdUtil.simpleUUID()+"."+extensionName;
				//使用hutool新的主键策略（字符串长度减少）：ObjectId是MongoDB数据库的一种唯一ID生成策略，是UUID version1的变种
				fileName = IdUtil.objectId()+"."+extensionName;
				myFileModel.setFileName(fileName);
				//先上传文件
				try {
					//获取文件最终的保存路径（物理路径）
					savePath=savePath+fileName;
					//将文件上传到指定路径
					file.transferTo(new File(savePath));
					//获取文件上传后的相对路径,
					myFileModel.setFileRelativeUrl(baseUploadUrl+fileName);
					//获取文件上传后的完整网址
					myFileModel.setFileUrl(RequestUtil.getBasePath()+myFileModel.getFileRelativeUrl());
					//获取文件上传后的完整物理路径
					myFileModel.setFilePath(savePath);
					//设置文件上传后的相对物理保存路径
					fileRelativePath.append(fileName);
					//存入数据库一般是这个字段
					myFileModel.setFileRelativePath(fileRelativePath.toString());
				} catch (IOException e) {
					throw new MyWebException("上传失败："+e.getMessage());
				}
			}
		}else{//如果没有传递file参数或是没有选择文件
			throw new MyWebException("请选择要上传的文件");//默认上传失败
		}
		return myFileModel;
	}

}
